import 'dart:convert';
import 'dart:io';

import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:collection/collection.dart';
import 'package:gen_global_config/gen_global_config.dart';
import 'package:source_gen/source_gen.dart';
import 'package:yaml/yaml.dart';

class GlobalConfigGenerator
    extends GeneratorForAnnotation<GlobalConfigAnnotation> {
  @override
  generateForAnnotatedElement(
    Element element,
    ConstantReader annotation,
    BuildStep buildStep,
  ) async {
    try {
      print("[gen_global_config]start");
      final generator = ClassGenerator();
      final now = DateTime.now().toIso8601String();
      final yamlAssetId = AssetId(buildStep.inputId.package, 'pubspec.yaml');
      final yamlContent = await buildStep.readAsString(yamlAssetId);
      var yamlMap = loadYaml(yamlContent);
      generator.generate(jsonDecode(jsonEncode(yamlMap)), 'YamlEntity');
      final className = element.name;
      final imports = element.source?.uri;
      var yamlJson = jsonEncode(yamlMap);

      String code =
          '''
import "$imports";
import "dart:io";
import 'dart:convert';
import 'package:gen_global_config/gen_global_config.dart';
Future<void> main() async {
  final instance = $className();
  if(instance is GlobalFileModifier){
    for (final f in (instance as GlobalFileModifier).functions) {
     var file=File(f.path);
     if(!file.existsSync()){
       print("[gen_global_config]file not exist:\${file.path}");
       continue;
     }
     print("[gen_global_config]modify:\${f.path}");
     var yamlMap=jsonDecode(r\'\'\'$yamlJson\'\'\');
      final res = await f.func(file.readAsStringSync(),yamlMap);
      if(res!=null){
        file.writeAsStringSync(res);
      }
    }
  }
}


''';
      var exeFile = File("lib/.exe.dart");
      exeFile.writeAsStringSync(code);
      final process = await Process.start('dart', ['run', 'lib/.exe.dart']);
      process.stdout.transform(utf8.decoder).listen((s) {
        print("[gen_global_config]stdout:$s");
      });
      process.stderr.transform(utf8.decoder).listen((s) {
        print("[gen_global_config]stderr:$s");
      });
      await process.exitCode;
      exeFile.delete();

      return """
    // Generate at：$now
    ${generator.result()}
    var yamlMap = YamlEntity.fromJson(${jsonEncode(yamlMap)});
    """;
    } catch (e) {
      print("[gen_global_config]$e");
    }
  }
}

Builder globalConfigBuilder(BuilderOptions options) =>
    LibraryBuilder(GlobalConfigGenerator(), generatedExtension: ".gen.dart");

class ClassGenerator {
  final Map<String, StringBuffer> _classes = {};
  final Map<String, Map<String, String>> _structSigs = {};
  // className -> {fieldName:type,...} 用于简单结构签名对比

  void generate(
    dynamic json,
    String rootClassName, {
    List<String> path = const [],
  }) {
    // 当前层实际类名（根层用 rootClassName，子层动态生成）
    final className = path.isEmpty ? rootClassName : _buildClassName(path);

    // 只有 Map 才生成类
    if (json is! Map<String, dynamic>) {
      return;
    }

    // 过滤 null
    final entries = json.entries.where((e) => e.value != null).toList();

    // 字段行与结构签名
    final sig = <String, String>{};
    final buffer = StringBuffer();
    buffer.writeln('class $className {');

    for (final e in entries) {
      final fieldName = _safeFieldName(e.key);
      final fieldType = _inferType(e.value, fieldName, [...path, e.key]);
      buffer.writeln('  final $fieldType $fieldName;');
      sig[fieldName] = fieldType;
    }

    // 构造函数
    buffer.write('\n  const $className({');
    buffer.write(
      entries.map((e) => 'required this.${_safeFieldName(e.key)}').join(', '),
    );
    buffer.writeln('});\n');

    // fromJson
    buffer.writeln(
      '  factory $className.fromJson(Map<String, dynamic> json) {',
    );
    buffer.writeln('    return $className(');
    for (final e in entries) {
      final fieldName = _safeFieldName(e.key);
      final fieldType = _inferType(e.value, fieldName, [...path, e.key]);
      final expr = _fromJsonExpr(fieldType, e.key);
      buffer.writeln('      $fieldName: $expr,');
    }
    buffer.writeln('    );');
    buffer.writeln('  }');

    buffer.writeln('}');

    // 注册类（处理重名冲突）
    final actualClassName = _ensureUniqueClassName(className, sig, buffer);
    if (actualClassName != className) {
      // 如果冲突被重命名，需把刚写的类名替换（简单处理：重写）
      final updated = buffer
          .toString()
          .replaceAll('class $className', 'class $actualClassName')
          .replaceAll(' $className({', ' $actualClassName({')
          .replaceAll(
            'factory $className.fromJson',
            'factory $actualClassName.fromJson',
          )
          .replaceAll('$className(', '$actualClassName(');
      _classes[actualClassName] = StringBuffer(updated);
    }

    // 递归子类
    for (final e in json.entries) {
      final v = e.value;
      final k = e.key;
      final nextPath = [...path, k];

      if (v is Map) {
        generate(v.cast<String, dynamic>(), rootClassName, path: nextPath);
      } else if (v is List && v.isNotEmpty) {
        // 找第一个 Map
        final firstMap = v.firstWhereOrNull((x) => x is Map);
        if (firstMap is Map) {
          generate(
            firstMap.cast<String, dynamic>(),
            rootClassName,
            path: nextPath,
          );
        }
      }
    }
  }

  /// 根据路径构造类名，如 ['dependencies','flutter'] -> DependenciesFlutter
  String _buildClassName(List<String> path) => path.map(_capitalize).join();

  /// 注册并避免类名冲突；结构不同才加后缀
  String _ensureUniqueClassName(
    String className,
    Map<String, String> sig,
    StringBuffer buf,
  ) {
    if (!_classes.containsKey(className)) {
      _classes[className] = buf;
      _structSigs[className] = sig;
      return className;
    }
    // 若结构相同，忽略重复生成
    final eq = const MapEquality<String, String>().equals;
    if (eq(_structSigs[className]!, sig)) {
      return className;
    }
    // 结构不同，加后缀
    var i = 2;
    var newName = '${className}_$i';
    while (_classes.containsKey(newName)) {
      i++;
      newName = '${className}_$i';
    }
    _classes[newName] = buf;
    _structSigs[newName] = sig;
    return newName;
  }

  /// 字段名合法化
  String _safeFieldName(String raw) {
    final s = raw.replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_');
    if (s.isEmpty) return 'unnamed';
    final start = RegExp(r'^[0-9]');
    return start.hasMatch(s) ? '_$s' : s;
  }

  String _inferType(dynamic value, String keyName, List<String> path) {
    if (value is int) return 'int';
    if (value is double) return 'double';
    if (value is bool) return 'bool';
    if (value is String) return 'String';

    if (value is List) {
      if (value.isEmpty) return 'List<dynamic>';

      final nonNull = value.where((e) => e != null).toList();
      if (nonNull.isEmpty) return 'List<dynamic>';

      final allMap = nonNull.every((e) => e is Map);
      if (allMap) return 'List<${_buildClassName(path)}>';

      final allString = nonNull.every((e) => e is String);
      if (allString) return 'List<String>';

      final allNum = nonNull.every((e) => e is int || e is double);
      if (allNum) return 'List<num>';

      final numAndString = nonNull.every(
        (e) => e is String || e is int || e is double,
      );
      if (numAndString) return 'List<String>'; // 文本+数字混合 → String

      return 'List<dynamic>';
    }

    if (value is Map) return _buildClassName(path);
    return 'dynamic';
  }

  String _fromJsonExpr(String type, String key) {
    if (type.startsWith('List<')) {
      final inner = type.substring(5, type.length - 1);
      if (_isPrimitive(inner) || inner == 'num') {
        return 'List<$inner>.from(json[\'$key\'] ?? [])';
      } else {
        return '(json[\'$key\'] as List?)?.map((x) => $inner.fromJson(Map<String,dynamic>.from(x as Map))).toList() ?? []';
      }
    } else if (_isPrimitive(type) || type == 'num') {
      return 'json[\'$key\']';
    } else {
      return '$type.fromJson(Map<String,dynamic>.from(json[\'$key\'] ?? const {}))';
    }
  }

  bool _isPrimitive(String t) =>
      const {'String', 'int', 'double', 'bool', 'dynamic'}.contains(t);

  String _capitalize(String s) =>
      s.isEmpty ? s : s[0].toUpperCase() + s.substring(1);

  String result() => _classes.values.map((b) => b.toString()).join('\n\n');
}
